package javalight.network.ro.server.udp;

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetSocketAddress;
import java.net.SocketException;
import java.util.Queue;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.LinkedBlockingDeque;

import javalight.util.LightTimer;

public class DatagramServer implements Runnable{

	boolean running = true;
	DatagramSocket server;
	Queue<DatagramPacket> queue = new LinkedBlockingDeque<DatagramPacket>();
	IDataHandler handler;
	ExecutorService threads;
	Throwable lastException;

	public DatagramServer(int port, int threadPoolSize, IDataHandler handler) throws SocketException
	{
		this("localhost", port, threadPoolSize, handler);
	}
	public DatagramServer(String host, int port, int threadPoolSize, IDataHandler handler) throws SocketException
	{
		this.handler = handler;
		server = new DatagramSocket(new InetSocketAddress(host, port));
		threads = Executors.newFixedThreadPool(threadPoolSize);
		for(int i=0;i<threadPoolSize;i++)
		{
	    	byte[] buf = new byte[1024*10];
	    	DatagramPacket packet = new DatagramPacket(buf, buf.length);
	    	queue.add(packet);
		}
	}
	
	public DatagramPacket receiveData() throws IOException
	{
		DatagramPacket packet = queue.poll();
		while(packet==null)
		{
			LightTimer.sleep(10);
			packet = queue.poll();
		}
    	server.receive(packet);
    	return packet;
	}

	@Override
	public void run() {
		while(running)
		{
			try {
				final DatagramPacket packet = receiveData();
				Runnable task = new Runnable(){
					public void run() {
						try {
							handler.onData(server, packet);
						} catch (IOException e) {
							lastException = e;
						}
						finally
						{
							queue.add(packet);
						}
					};
				};
				threads.execute(task);
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
	}
	
	public void start()
	{
		new Thread(this).start();
	}
	
	public void stop()
	{
		running = false;
	}
	
}
